<?php

namespace magein\region\lib;

class Spider
{

    /**
     * @var string
     */
    private $url = 'http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/';

    /**
     * @var string
     */
    private $year = '';

    /**
     * @var array
     */
    private $province = [];

    /**
     * @var array
     */
    private $city = [];

    /**
     * @var array
     */
    private $area = [];

    /**
     * @var array
     */
    private $street = [];

    /**
     * Spider constructor.
     * @param string $year
     */
    public function __construct($year = '')
    {
        if (empty($year)) {
            $this->year = date('Y') - 1;
        }
    }

    /**
     * @param $url
     * @return string
     * @throws \Exception
     */
    private function getUrl($url)
    {
        if ($this->year >= date('Y')) {
            throw new \Exception('获取失败');
        }

        return $this->url . $this->year . '/' . $url;
    }

    /**
     * @param $url
     * @param $page
     * @param bool $force
     * @return false|string
     * @throws \Exception
     */
    private function get($url, $page, $force = false)
    {
        $dir = './page/' . $this->year . '/';

        if (!is_dir($dir)) {
            mkdir($dir, 0777);
        }

        $path = $dir . $page . '.html';

        if (is_file($path) && $force === false) {

            $content = file_get_contents($path);

        } else {

            $url = $this->getUrl($url);

            $content = file_get_contents($url);

            if (false === $content) {

                $error_log = './error/' . $this->year . '.log';

                if (is_file($error_log)) {
                    // 防止重复加入到错误日志用
                    $error = file_get_contents($error_log);

                    if ($error) {
                        preg_match('/[0-9]+.html/', $url, $code);
                        if (preg_match("/$code[0]/", $error)) {
                            return '';
                        }
                    }
                }

                file_put_contents($error_log, $url . "\n", FILE_APPEND);

                return '';
            }

            $content = iconv('gb2312', 'utf-8//IGNORE', $content);

            file_put_contents($path, $content);
        }

        return $content;
    }

    /**
     * 检测文件是否是空的，用于排除请求失败的问题
     * @param bool $remove
     */
    public function check($remove = false)
    {
        $page = glob('./page/' . $this->year . '/*.html');

        $exec = function ($path) use ($remove) {
            echo $path . '<br>';
            if ($remove) {
                unlink($path);
            };
        };

        if ($page) {
            foreach ($page as $item) {

                if (filesize($item) == 0) {
                    $exec($item);
                    continue;
                }

                $content = file_get_contents($item);
                if (preg_match('/captcha/', $content)) {
                    $exec($item);
                    continue;
                }

                if (preg_match('/noscript/', $content)) {
                    $exec($item);
                    continue;
                }
            }
        }
    }

    /**
     * @param bool $force
     * @return array
     * @throws \Exception
     */
    protected function getProvince($force = false)
    {
        $url = 'index.html';

        $content = $this->get($url, 'province');

        preg_match_all('/<a href=\'([0-9]+).html\'>(.{3,18})<br/', $content, $matches);

        // 获取以区域ID为键的省份信息
        if (isset($matches[1]) && isset($matches[2])) {
            foreach ($matches[1] as $key => $item) {
                $this->province[$item] = $matches[2][$key];
            }
        }

        return $this->province;
    }

    /**
     * @param $provinceId
     * @return false|string
     * @throws \Exception
     */
    protected function getCity($provinceId)
    {
        $provinceId = intval($provinceId);

        $url = $provinceId . '.html';

        $content = $this->get($url, $provinceId);

        return $content;
    }

    /**
     * @param $cityId
     * @return false|string
     * @throws \Exception
     */
    protected function getArea($cityId)
    {
        $province = substr($cityId, 0, 2);
        $city = substr($cityId, 0, 4);

        $url = $province . '/' . $city . '.html';

        return $this->get($url, $city);
    }

    /**
     * @param $areaId
     * @return false|string
     * @throws \Exception
     */
    protected function getStreet($areaId)
    {
        $province = substr($areaId, 0, 2);
        $city = substr($areaId, 2, 2);
        $area = substr($areaId, 0, 6);

        $url = $province . '/' . $city . '/' . $area . '.html';

        return $this->get($url, $area);
    }

    /**
     * @param $string
     * @return array
     */
    private function getArray($string)
    {
        $result = [];

        if ($string) {

            $string = preg_replace('/href=\'[0-9\/]+|[<>a-z\'\s\/=.]/', '', $string);

            $string = preg_replace('/([0-9]{12}[^0-9]+)/', '$1,', $string);

            $string = trim($string, ',');

            $array = explode(',', $string);

            $array = array_filter($array);

            if ($array) {
                foreach ($array as $item) {
                    $result[] = [
                        'id' => substr($item, 0, 12),
                        'name' => substr($item, 12)
                    ];
                }
            }
        }

        return $result;
    }

    /**
     * @throws \Exception
     */
    protected function getPage()
    {
        $this->getProvince();

        if ($this->province) {

            foreach ($this->province as $key => $item) {

                $content = $this->getCity($key);

                if ($content) {

                    preg_match('/citytr[\s\S]+<\/tr>/', $content, $matches);

                    if (isset($matches[0]) && $matches[0]) {
                        $this->city[$key] = $this->getArray($matches[0]);
                    }
                }
            }
        }

        if ($this->city) {
            foreach ($this->city as $key => $city) {

                foreach ($city as $item) {

                    if (isset($item['id'])) {

                        $content = $this->getArea($item['id']);

                        if ($content) {

                            preg_match('/countytr[\s\S]+<\/tr>/', $content, $matches);

                            if (isset($matches[0]) && $matches[0]) {
                                $this->area[$item['id']] = $this->getArray($matches[0]);
                            }
                        }
                    }
                }
            }
        }

        if ($this->area) {
            foreach ($this->area as $key => $area) {
                foreach ($area as $item) {

                    if (isset($item['id'])) {

                        $content = $this->getStreet($item['id']);

                        if ($content) {
                            preg_match('/towntr[\s\S]+<\/tr>/', $content, $matches);
                            if (isset($matches[0]) && $matches[0]) {
                                $this->street[$item['id']] = $this->getArray($matches[0]);
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * @return array
     * @throws \Exception
     */
    public function getData()
    {
        $result = [];

        if (empty($this->province)) {
            $this->getPage();
        }

        if ($this->province) {

            $result = [];
            foreach ($this->province as $id => $province) {
                $id = str_pad($id, 6, '0', STR_PAD_RIGHT);
                $result[] = [
                    'id' => $id,
                    'pid' => 0,
                    'name' => $province,
                ];
            }

            if ($this->city) {
                foreach ($this->city as $city) {

                    foreach ($city as $item) {
                        $pid = substr($item['id'], 0, 2) . '0000';

                        $result[] = [
                            'id' => substr($item['id'], 0, 6),
                            'pid' => $pid,
                            'name' => $item['name']
                        ];
                    }

                }
            }

            if ($this->area) {
                foreach ($this->area as $area) {
                    foreach ($area as $item) {
                        $pid = substr($item['id'], 0, 4) . '00';
                        $result[] = [
                            'id' => substr($item['id'], 0, 6),
                            'pid' => $pid,
                            'name' => $item['name']
                        ];
                    }
                }
            }

        }

        return $result;
    }
}